package com.example.data.crawler;

import com.example.data.entity.RawData;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Component;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 秦朝史料定向爬虫（适配 https://zhonghua.5000yan.com/qinchao/ 页面结构）
 * 功能：提取章节标题、详情页URL、完整正文内容，用于后续数据清洗
 */
@Component
public class WebCrawler {
    // 目标网站根URL（秦朝史料首页）
    public static final String QIN_ROOT_URL = "https://zhonghua.5000yan.com/qinchao/";
    // HTTP客户端（复用连接，减少开销）
    private final HttpClient httpClient;
    private static final Logger log = LoggerFactory.getLogger(WebCrawler.class);

    // 构造方法：初始化HTTP客户端（配置反爬与超时）
    public WebCrawler() {
        this.httpClient = HttpClient.newBuilder()
                .connectTimeout(Duration.ofSeconds(10))  // 连接超时10秒
                .followRedirects(HttpClient.Redirect.NORMAL)  // 自动跟随重定向
                .build();
    }

    /**
     * 步骤1：爬取首页HTML（获取章节列表入口）
     */
    private String fetchHomePageHtml() throws Exception {


        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(QIN_ROOT_URL))
                // 伪装浏览器请求头，避免被识别为爬虫
                .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36")
                .header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
                .timeout(Duration.ofSeconds(15))  // 响应超时15秒
                .GET()
                .build();

        HttpResponse<String> response = httpClient.send(
                request,
                HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)  // 强制UTF-8编码，避免中文乱码
        );

        // 校验响应状态（非200则请求失败）
        if (response.statusCode() != 200) {
            throw new RuntimeException("首页请求失败，URL：" + QIN_ROOT_URL + "，状态码：" + response.statusCode());
        }
        log.info("成功获取首页HTML，内容长度：{} 字符", response.body().length());
        return response.body();
    }

    /**
     * 步骤2：从首页解析章节标题和详情页URL（核心：匹配首页<a>标签结构）
     */
    private List<RawData> parseChapterTitleAndUrl(String homePageHtml) {
        List<RawData> chapterList = new ArrayList<>();
        Document doc = Jsoup.parse(homePageHtml);

        // 关键选择器：匹配首页中所有章节的<a>标签（完全对应页面HTML结构）
        // 页面结构：<div class="list-group-flush">下的<a class="list-group-item list-group-item-action py-4">
        Elements chapterLinks = doc.select("div.list-group-flush > a.list-group-item.list-group-item-action.py-4");

        if (chapterLinks.isEmpty()) {
            log.error("未找到任何章节链接，可能网站结构已变更！当前使用选择器：div.list-group-flush > a.list-group-item.list-group-item-action.py-4");
            return chapterList;
        }

        log.info("首页解析到 {} 个章节链接，开始提取信息...", chapterLinks.size());
        for (Element link : chapterLinks) {
            RawData rawData = new RawData();

            // 2.1 提取章节标题（<a>标签内的<h5 class="mb-1 lh-base">）
            Element titleElem = link.selectFirst("h5.mb-1.lh-base");  // 匹配页面标题标签结构
            String chapterTitle = titleElem != null ? titleElem.text().trim() : "未知章节";
            rawData.setChapterTitle(chapterTitle);

            // 2.2 提取章节详情页URL（<a>标签的href属性，页面中已为完整URL）
            String chapterUrl = link.attr("href").trim();
            // 过滤无效URL（仅保留秦朝史料相关链接）
            if (chapterUrl.startsWith("https://zhonghua.5000yan.com/qinchao/")) {
                rawData.setSourceUrl(chapterUrl);
                chapterList.add(rawData);
                log.debug("解析章节：{} -> 详情页URL：{}", chapterTitle, chapterUrl);
            } else {
                log.warn("跳过非目标章节URL：{}（标题：{}）", chapterUrl, chapterTitle);
            }
        }
        log.info("首页解析完成，共获取 {} 个有效章节", chapterList.size());
        return chapterList;
    }

    /**
     * 步骤3：爬取单个章节详情页，提取完整正文（核心：匹配详情页<div class="grap">结构）
     */
    private void fetchChapterContent(RawData rawData) throws Exception {


        String chapterUrl = rawData.getSourceUrl();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(chapterUrl))
                // 补充Referer头，模拟从首页跳转详情页，提升反爬通过率
                .header("Referer", QIN_ROOT_URL)
                .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36")
                .header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
                .timeout(Duration.ofSeconds(15))
                .GET()
                .build();

        HttpResponse<String> response = httpClient.send(
                request,
                HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)
        );

        if (response.statusCode() != 200) {
            throw new RuntimeException("详情页请求失败，URL：" + chapterUrl + "，状态码：" + response.statusCode());
        }

        // 解析详情页HTML，提取正文
        Document doc = Jsoup.parse(response.body());

        // 在 fetchChapterContent 方法中，获取 response.body() 后添加日志
        String html = response.body();
        log.debug("详情页HTML片段（前2000字符）：{}", html.length() > 2000 ? html.substring(0, 2000) : html);

// 检查是否包含目标容器
        if (!html.contains("class=\"grap\"")) {
            log.error("详情页HTML中未找到 div.grap 容器！URL：{}", chapterUrl);
        }
        // 关键选择器：匹配详情页中正文根容器（页面结构：<div class="grap">包含所有正文段落）
        Element contentContainer = doc.selectFirst("div.grap");
        if (contentContainer == null) {
            log.warn("详情页未找到正文容器，使用页面全文降级处理，URL：{}", chapterUrl);
            // 降级方案：提取body内所有文本（过滤脚本、样式等无关内容）
            rawData.setOriginalText(doc.body().text().trim().replaceAll("\\s+", " "));
            return;
        }

        // 提取容器内所有正文段落（<div.grap>下的子<div>标签，每个对应一段正文）
        Elements contentParagraphs = contentContainer.select("div");
        StringBuilder fullContent = new StringBuilder();
        for (Element paragraph : contentParagraphs) {
            String paraText = paragraph.text().trim();
            if (!paraText.isEmpty()) {  // 过滤空段落
                fullContent.append(paraText).append("\n");  // 段落间用换行分隔，保留原文结构
            }
        }

        // 补充详情页标题（优先用详情页标题，比首页标题更精准）
        Element detailTitleElem = doc.selectFirst("h5.py-3.lh-base.text-center");  // 匹配详情页标题标签
        if (detailTitleElem != null && !detailTitleElem.text().trim().isEmpty()) {
            rawData.setChapterTitle(detailTitleElem.text().trim());
        }

        // 设置完整正文和爬取时间
        rawData.setOriginalText(fullContent.toString().trim());
        rawData.setCrawlTime(LocalDateTime.now());
        rawData.setIsValid(1);  // 标记为有效数据
        log.info("成功爬取章节：{}（正文长度：{} 字符，URL：{}）",
                rawData.getChapterTitle(), rawData.getOriginalText().length(), chapterUrl);
    }

    /**
     * 核心入口：爬取所有秦朝史料章节（标题+URL+完整正文）
     */
    public List<RawData> crawlAllQinChapters() throws Exception {
        // 1. 爬取首页并解析章节标题和URL
        String homePageHtml = fetchHomePageHtml();
        List<RawData> chapterList = parseChapterTitleAndUrl(homePageHtml);

        if (chapterList.isEmpty()) {
            throw new RuntimeException("未获取到有效章节信息，爬取终止");
        }

        // 2. 逐个爬取章节详情页内容（添加反爬间隔）
        for (RawData chapter : chapterList) {
            try {
                fetchChapterContent(chapter);
                // 反爬控制：每爬取1个章节暂停1.5秒，避免高频请求被封IP
                Thread.sleep(1500);
            } catch (Exception e) {
                log.error("爬取章节失败，URL：{}，错误原因：{}", chapter.getSourceUrl(), e.getMessage());
                chapter.setIsValid(0);  // 标记为无效数据，便于后续过滤
            }
        }

        log.info("全量爬取完成，共处理 {} 个章节（有效：{} 个，无效：{} 个）",
                chapterList.size(),
                chapterList.stream().filter(c -> c.getIsValid() == 1).count(),
                chapterList.stream().filter(c -> c.getIsValid() == 0).count());
        return chapterList;
    }

    /**
     * 工具方法：单独爬取指定章节（供测试用）
     */
    public RawData crawlSingleChapter(String chapterUrl) throws Exception {
        RawData rawData = new RawData();
        rawData.setSourceUrl(chapterUrl);
        fetchChapterContent(rawData);
        return rawData;
    }
}